using System;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using Lazy.Captcha.Core;
using Lucene.Net.Util;
using Microsoft.AspNetCore.Http;
using MySqlX.XDevAPI.Common;
using Publicuse.Appication;
using Publicuse.Cache;
using Publicuse.Entity;
using Publicuse.Entity.other;
using Publicuse.Interface;
using Publicuse.Util;
using Publicuse.Util.Jwt;

namespace Geekeradmin.Common
{
    /// <summary>
    /// 用户登录验证
    /// </summary>
    public static class LoginManage
    {
        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="context"></param>
        /// <param name="authorityApi"></param>
        /// <param name="mySqlBaseService"></param>
        /// <param name="msg"></param>
        /// <param name="userLoginResult"></param>
        /// <param name="timestamp"></param>
        /// <param name="loginretrytime"></param>
        /// <param name="jWTService"></param>
        /// <param name="name"></param>
        /// <param name="pwd"></param>
        /// <param name="verify"></param>
        /// <returns></returns>
        public static ImplementResult UserLogin(
            this HttpContext context,
            DBConnectionOption authorityApi,
            IMySqlSugarService mySqlBaseService,
            out string msg,
            out UserLoginResult userLoginResult,
            string timestamp,
            out string loginretrytime,
            out int errcount,
            out int sycs,
            ICustomJWTService jWTService,
            string name = "",
            string pwd = "",
            string verify = ""
        )
        {
            userLoginResult = new UserLoginResult();
            msg = string.Empty;
            loginretrytime = "0";
            errcount = 0;
            sycs = 1;
            if (string.IsNullOrEmpty(timestamp))
            {
                return ImplementResult.VerifyErr;
            }
            var ymzdata = MemoryUtil.GetMemory(timestamp);
            if (ymzdata == null)
            {
                return ImplementResult.UserVerify;
            }
            var yzm = MemoryUtil.GetMemory(timestamp)!.ToString();
            if (
                string.IsNullOrEmpty(yzm)
                || string.Compare(yzm.ToLower(), verify.ToLower(), true) != 0
            )
            {
                return ImplementResult.UserVerify;
            }
            else
            {
                MemoryUtil.RemoveMemory(timestamp);
            }
            var usertable = TableAtributeUtil.GetTable<User>();
            name = EnDecryptionUtil.DESDecryptbyqd(name);
            string sql =
                $"select LoginType,uCode,a.status,uPwd,a.Id,retrycount, retrytime from {usertable} a  where uCode='{name}';";
            var dt = mySqlBaseService.GetDataTable(sql, null, out msg);
            if (!msg.IsNull())
            {
                return ImplementResult.DBError;
            }
            string uPwd = string.Empty;
            if (dt.Rows.Count == 0)
            {
                return ImplementResult.UserPwd;
            }
            else
            {
                if (dt.Rows.Count > 0)
                {
                    var havedata = dt.Select("status=1").Length;
                    if (havedata == 0)
                    {
                        var retrycount = dt.Rows[0]["retrycount"].ToString().GetIntValue();
                        if (retrycount >= 5)
                        {
                            errcount = retrycount;
                            return ImplementResult.UserPwdStop;
                        }
                        else
                        {
                            return ImplementResult.UserStop;
                        }
                    }
                }
                var status = Convert.ToInt32(dt.Select("status=1")[0]["status"]);
                if (status != 1)
                {
                    var retrycount = dt.Select("status=1")[0]["retrycount"]
                        .ToString()
                        .GetIntValue();
                    if (retrycount >= 5)
                    {
                        errcount = retrycount;
                        return ImplementResult.UserPwdStop;
                    }
                    else
                    {
                        return ImplementResult.UserStop;
                    }
                }
                else
                {
                    var systable = TableAtributeUtil.GetTable<Spconf>();
                    var pwdretry = "0";
                    sql =
                        $"select sCode,sValue from {systable} where sCode in('pwdretry','loginretrytime') and status<>-9; ";
                    var dtb = mySqlBaseService.GetDataTable(sql, null, out var msgb);
                    if (string.IsNullOrEmpty(msgb))
                    {
                        if (dtb.Rows.Count > 0)
                        {
                            var sCodea = dtb.Rows[0]["sCode"].ToString();
                            pwdretry = dtb.Rows[0]["sValue"].ToString() ?? "5"; //重试5次
                            errcount = pwdretry.GetIntValue();

                            var sCodeb = dtb.Rows[1]["sCode"].ToString();
                            loginretrytime = dtb.Rows[1]["sValue"].ToString() ?? "5分钟"; //休息5分钟
                        }
                    }
                    var retrycount = dt.Select("status=1")[0]["retrycount"]
                        .ToString()
                        .GetIntValue();
                    var retrytime = dt.Select("status=1")[0]["retrytime"].ToString();
                    if (!string.IsNullOrEmpty(retrytime))
                    {
                        var newretrytime = Convert.ToDateTime(retrytime);
                        var sjt = DateTime.Now - newretrytime;
                        if (sjt.Minutes >= 5 || sjt.Hours > 0 || sjt.Days > 0)
                        {
                            retrycount = 0;
                            sql =
                                $"update {usertable} set retrycount=0,retrytime='{DateTime.Now}' where uCode='{name}'  and status<>-9;";
                            mySqlBaseService.ExecuteSql(sql, null, out var msgc);
                        }
                        else
                        {
                            var Minutes = sjt.Minutes;
                            var Seconds = 0;
                            if (sjt.Minutes == 0)
                            {
                                Minutes = pwdretry.GetIntValue() - 1;
                                Seconds = 60 - sjt.Seconds;
                            }
                            else
                            {
                                Minutes = pwdretry.GetIntValue() - Minutes;
                                Seconds = 60 - sjt.Seconds;
                            }
                            loginretrytime = Minutes + "分钟" + Seconds + "秒";
                        }
                    }
                    else
                    {
                        sql =
                            $"update {usertable} set retrytime='{DateTime.Now}' where uCode='{name}'  and status<>-9;";
                        mySqlBaseService.ExecuteSql(sql, null, out var msgc);
                    }
                    if (retrycount > 0 && retrycount == pwdretry.GetIntValue())
                    {
                        return ImplementResult.UserRetrycountError;
                    }
                    pwd = EnDecryptionUtil.DESDecryptbyqd(pwd);
                    //pwd = EnDecryptionUtil.AesEncrypt(pwd);
                    pwd = AesEncryptionUtil.Encrypt(pwd);
                    uPwd = dt.Select("status=1")[0]["uPwd"].ToString();
                    if (string.Compare(uPwd, pwd, true) == 0)
                    {
                        sql = $"update {usertable} set retrycount=0 where uCode='{name}';";
                        mySqlBaseService.ExecuteSql(sql, null, out var msgc);
                        var user = new User();
                        var Id = Convert.ToInt32(dt.Select("status=1")[0]["Id"]);
                        var userrole = TableAtributeUtil.GetTable<UserRole>();
                        var roletb = TableAtributeUtil.GetTable<Role>();
                        var sqlb =
                            $"select a.*,r.rName,r.Id as rId from {usertable} a left join {userrole} b on a.Id=b.uId left join {roletb} r on FIND_IN_SET(r.Id,b.rids) where uId={Id};";
                        var userlist = mySqlBaseService.Query<User>(sqlb, out msg);
                        if (string.IsNullOrEmpty(msg) && userlist != null && userlist.Count > 0)
                        {
                            user = userlist.FirstOrDefault();
                            //var jmpwd = EnDecryptionUtil.AesDecrypt(user.uPwd);
                            //user.uPwd = EnDecryptionUtil.DESEncryptionb(jmpwd);
                            user.rName = String.Join(",", userlist.Select(o => o.rName).Distinct());
                            user.rId = String.Join(",", userlist.Select(o => o.rId).Distinct());

                            var diclist = UserAuthorCache.GetDictionariescache();
                            if (diclist != null && diclist.Count > 0)
                            {
                                if (!string.IsNullOrEmpty(user.pca))
                                {
                                    List<string> dicaname = new List<string>();
                                    foreach (var itemb in user.pca.Split(','))
                                    {
                                        var dica = diclist.Where(o => o.Id == itemb.GetIntValue());
                                        if (dica.Count() > 0)
                                        {
                                            dicaname.Add(dica.FirstOrDefault().dName);
                                        }
                                    }
                                    if (dicaname.Count > 0)
                                    {
                                        user.pcaremark = String.Join("", dicaname);
                                    }
                                }
                            }
                        }
                        var spctable = TableAtributeUtil.GetTable<Spconf>();
                        sql = $"select sValue from {spctable} where sCode='SysTimeOut' ";
                        int SysTimeOut = 0;
                        var data = mySqlBaseService.Query<Spconf>(sql, out msg);
                        if (data.Count() > 0)
                        {
                            try
                            {
                                SysTimeOut = Convert.ToInt32(data.FirstOrDefault().sValue);
                            }
                            catch (Exception e)
                            {
                                SysTimeOut = 180;
                            }
                        }

                        var sqlpwd =
                            $"select lasttimeupw,resetpwd from {usertable} where Id='{user.Id}' and status<>-9;";
                        var userdata = mySqlBaseService.Query<User>(sqlpwd, out var msgpwd);
                        if (string.IsNullOrEmpty(msgpwd) && userdata != null && userdata.Count > 0)
                        {
                            var lasttimeupw = userdata.FirstOrDefault().lasttimeupw;
                            var resetpwd = userdata.FirstOrDefault().resetpwd;
                            if (string.IsNullOrEmpty(lasttimeupw))
                            {
                                userLoginResult.isNeedUpdatePwd = true;
                                userLoginResult.needUpdatePwdMsg = "，首次登录需要先修改密码,请先修改密码！";
                            }
                            if (string.Compare(resetpwd, "是", true) == 0)
                            {
                                userLoginResult.isNeedUpdatePwd = true;
                                userLoginResult.needUpdatePwdMsg = "，您的账号已重置密码,请先修改密码！";
                            }
                        }

                        userLoginResult.uploadyhead = user.uploadyhead;
                        if (string.Compare(name, "ajng", true) == 0)
                        {
                            List<Menu> list = new List<Menu>();
                            var authority = TableAtributeUtil.GetTable<Menu>();
                            sql = $"select * from {authority} where status=1;";
                            list = mySqlBaseService.Query<Menu>(sql, out msg);
                            userLoginResult.rIds = "1";
                            userLoginResult.rName = user.rName;
                            userLoginResult.user = user;
                            userLoginResult.list = list.Where(o=>o.isShow=="是").OrderBy(o => o.amoduleIndex).ToList();
                            var authtoken = String.Join(
                                ",",
                                userLoginResult.list.Select(o => o.aCode)
                            );
                            JwtToken jwtToken = new JwtToken();
                            userLoginResult.token = jWTService.GetToken(
                                user,
                                "",
                                SysTimeOut,
                                out msg,
                                out jwtToken
                            );
                            JwtToken jwtTokenb = new JwtToken();
                            userLoginResult.authtoken = jWTService.GetToken(
                                user,
                                authtoken,
                                SysTimeOut,
                                out msg,
                                out jwtTokenb                               
                            );
                            LoginCache.GetLoginCache.AddLoginMenuList(
                                userLoginResult.token,
                                userLoginResult.list
                            );
                            List<UserAction>? auUserActions = new List<UserAction>();
                            var actionlist = list.Where(o => o.aType == 3);
                            auUserActions =
                                actionlist.MapToList<UserAction, Menu>(o =>
                                {
                                    o.ForMember(d => d.url, opt => opt.MapFrom(s => s.aapiUrl));
                                    o.ForMember(d => d.name, opt => opt.MapFrom(s => s.aName));
                                }) as List<UserAction>;
                            CityCache.CacheCityData(mySqlBaseService);
                            LoginCache.GetLoginCache.SaveLoginInfo(
                                userLoginResult.token,
                                userLoginResult.rName,
                                userLoginResult.rIds,
                                userLoginResult.user.Id,
                                userLoginResult.user.uPwd,
                                mySqlBaseService
                            );
                            LoginCache.GetLoginCache.AddsysLogins(
                                Publicuse.Util.LogClass.GetIPAddress(),
                                name,
                                DateTime.Now,
                                userLoginResult.token
                            );
                            LoginCache.GetLoginCache.AddJwtToken(
                                userLoginResult.token,
                                mySqlBaseService
                            );
                            LoginCache.GetLoginCache.AddUserPass(userLoginResult.token, user.uPwd);
                            LoginCache.GetLoginCache.SaveLoginAction(
                                userLoginResult.token,
                                auUserActions
                            );
                            LoginCache.GetLoginCache.SaveLoginActionList(
                                userLoginResult.token,
                                auUserActions
                            );
                            var usql =
                                $"update {usertable} set logintime='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}' where uCode='{name}' and status<>-9;";
                            mySqlBaseService.ExecuteSql(usql, null, out msg);

                            return ImplementResult.UserSuccess;
                        }
                        var LoginType = dt.Rows[0]["LoginType"].ToString();
                        if (string.Compare(LoginType, "小程序登录", true) == 0)
                        {
                            return ImplementResult.LoginErr;
                        }
                        var userRole = TableAtributeUtil.GetTable<UserRole>();
                        sql = $"select rids from {userRole} where uId='{Id}';";
                        var userRoleData = mySqlBaseService.Query<UserRole>(sql, out msg);
                        if (userRoleData.Count == 0)
                        {
                            return ImplementResult.UserRoleVague;
                        }
                        else
                        {
                            List<Menu> list = new List<Menu>();
                            var rIds = String.Join(",", userRoleData.Select(o => o.rids));
                            var userResource = TableAtributeUtil.GetTable<URPermission>();
                            sql =
                                $"select aIds from {userResource} where uId='{Id}' and rId in({rIds});";
                            var userResourceData = mySqlBaseService.Query<URPermission>(
                                sql,
                                out msg
                            );
                            List<RoleResource> roleResourceData = new List<RoleResource>();
                            string aids = string.Empty;
                            if (userResourceData.Count == 0)
                            {
                                var roleresource = TableAtributeUtil.GetTable<RoleResource>();
                                sql = $"select aIds from {roleresource} where  rId in({rIds});";
                                var roleresourcedata = mySqlBaseService.Query<RoleResource>(
                                    sql,
                                    out msg
                                );
                                if (roleresourcedata.Count == 0)
                                {
                                    return ImplementResult.UserResourceVague;
                                }
                                aids = String
                                    .Join(",", roleresourcedata.Select(o => o.aIds).Distinct())
                                    .TrimEnd(',');
                            }
                            else
                            {
                                aids = String
                                    .Join(",", userResourceData.Select(o => o.aIds).Distinct())
                                    .TrimEnd(',');

                                var roleresource = TableAtributeUtil.GetTable<RoleResource>();
                                sql = $"select aIds from {roleresource} where  rId in({rIds});";
                                roleResourceData = mySqlBaseService.Query<RoleResource>(
                                    sql,
                                    out msg
                                );
                                if (roleResourceData.Count > 0)
                                {
                                    var newaids = String
                                        .Join(",", roleResourceData.Select(o => o.aIds).Distinct())
                                        .TrimEnd(',');
                                    aids = aids + "," + newaids;
                                    aids = String.Join(",", aids.Split(','));
                                }
                            }
                            var authority = TableAtributeUtil.GetTable<Menu>();
                            sql = $"select * from {authority} where Id in ({aids}) and status=1;";
                            list = mySqlBaseService.Query<Menu>(sql, out msg);
                            if (list.Count == 0)
                            {
                                return ImplementResult.UserResourceVague;
                            }
                            else
                            {
                                List<Menu> listb = new List<Menu>();
                                var sqlall = $"select * from {authority} where status<>-9;";
                                List<Menu> alllist = mySqlBaseService.Query<Menu>(sqlall, out msg);
                                foreach (var item in list)
                                {
                                    if (item.aType != 1)
                                    {
                                        var parentdata = alllist
                                            .Where(o => o.Id == item.aParentId)
                                            .ToList();
                                        if (string.IsNullOrEmpty(msg) && parentdata.Count > 0)
                                        {
                                            if (
                                                !listb.Any(o =>
                                                    o.Id == parentdata.FirstOrDefault().Id
                                                )
                                            )
                                            {
                                                if (string.Compare(parentdata.FirstOrDefault().isShow, "是", true) == 0)
                                                {
                                                    listb.Add(parentdata.FirstOrDefault());
                                                    var parentdatab = alllist
                                                        .Where(o =>
                                                            o.Id
                                                            == parentdata.FirstOrDefault().aParentId
                                                        )
                                                        .ToList();
                                                    if (
                                                        string.IsNullOrEmpty(msg)
                                                        && parentdata.Count > 0
                                                    )
                                                    {
                                                        AddParentData(
                                                            mySqlBaseService,
                                                            parentdata.FirstOrDefault().aParentId,
                                                            ref listb,
                                                            alllist
                                                        );
                                                    }
                                                }
                                            }
                                        }
                                        if (string.Compare(item.isShow, "是", true) == 0)
                                        {
                                            listb.Add(item);
                                        }
                                    }
                                    else
                                    {
                                        if (string.Compare(item.isShow, "是", true) == 0)
                                        {
                                            listb.Add(item);
                                        }
                                    }
                                }
                                list = listb.DistinctBy(o => o.Id).ToList();

                                List<UserAction> auUserActions = new List<UserAction>();
                                var actionlist = list.Where(o => (o.aType == 3));
                                auUserActions =
                                    actionlist.MapToList<UserAction, Menu>(o =>
                                    {
                                        o.ForMember(d => d.url, opt => opt.MapFrom(s => s.aapiUrl));
                                        o.ForMember(d => d.name, opt => opt.MapFrom(s => s.aName));
                                    }) as List<UserAction>;
                                userLoginResult.rIds = rIds;
                                userLoginResult.user = user;
                                userLoginResult.list = list.OrderBy(o => o.amoduleIndex).ToList();

                                var authtoken = String.Join(
                                    ",",
                                    userLoginResult.list.Select(o => o.aCode)
                                );
                                JwtToken jwtToken = new JwtToken();
                                userLoginResult.token = jWTService.GetToken(
                                    user,
                                    "",
                                    SysTimeOut,
                                    out msg,
                                    out jwtToken
                                );
                                JwtToken jwtTokenb = new JwtToken();
                                userLoginResult.authtoken = jWTService.GetToken(
                                    user,
                                    authtoken,
                                    SysTimeOut,
                                    out msg,
                                    out jwtTokenb
                                );
                                LoginCache.GetLoginCache.AddLoginMenuList(
                                    userLoginResult.token,
                                    userLoginResult.list
                                );
                                LoginCache.GetLoginCache.AddLoginMenuList(
                                    userLoginResult.token,
                                    userLoginResult.list
                                );
                                LoginCache.GetLoginCache.SaveLoginInfo(
                                    userLoginResult.token,
                                    userLoginResult.rName,
                                    userLoginResult.rIds,
                                    userLoginResult.user.Id,
                                    userLoginResult.user.uPwd,
                                    mySqlBaseService
                                );
                                LoginCache.GetLoginCache.AddsysLogins(
                                    Publicuse.Util.LogClass.GetIPAddress(),
                                    name,
                                    DateTime.Now,
                                    userLoginResult.token
                                );
                                LoginCache.GetLoginCache.AddJwtToken(
                                    userLoginResult.token,
                                    mySqlBaseService
                                );
                                LoginCache.GetLoginCache.AddUserPass(
                                    userLoginResult.token,
                                    user.uPwd
                                );
                                LoginCache.GetLoginCache.SaveLoginAction(
                                    userLoginResult.token,
                                    auUserActions
                                );
                                LoginCache.GetLoginCache.SaveLoginActionList(
                                    userLoginResult.token,
                                    auUserActions
                                );
                            }
                        }
                        LoginCache.GetLoginCache.RemoveUserLoginYzm(timestamp);
                        LoginCache.GetLoginCache.RemoveUserLoginYzmSuccess(timestamp);
                        var usqlb =
                            $"update {usertable} set logintime='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}' where uCode='{name}';";
                        mySqlBaseService.ExecuteSql(usqlb, null, out msg);
                        return ImplementResult.UserSuccess;
                    }
                    else
                    {
                        sycs = errcount - (retrycount + 1);
                        if (sycs == 0 || retrycount >= pwdretry.GetIntValue())
                        {
                            sql =
                                $"update {usertable} set status=2,retrycount=retrycount+1 where uCode='{name}';";
                            mySqlBaseService.ExecuteSql(sql, null, out var msgc);
                            var lcontent =
                                $"用户{name}登录系统密码错误次数已超过限制{errcount}次，今日将锁定账号不能登录系统，请联系管理员！";
                            Log log = new Log()
                            {
                                lmodule = "锁定账号",
                                ltype = "post",
                                luser = name,
                                lsuccess = 1,
                                status = 1,
                                lrole = name,
                                lcontent = lcontent
                            };
                            mySqlBaseService.Insert<Log>(log, out msg);
                            return ImplementResult.UserPwdStop;
                        }
                        else
                        {
                            sql =
                                $"update {usertable} set retrycount=retrycount+1 where uCode='{name}';";
                            mySqlBaseService.ExecuteSql(sql, null, out var msgc);
                            return ImplementResult.UserPwd;
                        }
                    }
                }
            }
        }

        private static void AddParentData(
            IMySqlSugarService mySqlBaseService,
            long pid,
            ref List<Menu> authorities,
            List<Menu> alllist
        )
        {
            var parentdata = alllist.Where(o => o.Id == pid).ToList();
            if (parentdata.Count > 0)
            {
                if (!authorities.Any(o => o.Id == parentdata.FirstOrDefault().Id))
                {
                    authorities.Add(parentdata.FirstOrDefault());
                }
                AddParentData(
                    mySqlBaseService,
                    parentdata.FirstOrDefault().aParentId,
                    ref authorities,
                    alllist
                );
            }
        }

        /// <summary>
        /// 获取随机验证码
        /// </summary>
        /// <param name="strlength"></param>
        /// <param name="checkNum"></param>
        /// <returns></returns>
        public static LoginYZM CreateRegionCode(int strlength, int checkNum)
        {
            var data =
                @"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890中华绝艺世界奇珍法天象地抱阳负阴用智骋材悦性养心渊澄流远叶茂根深滥觞既夐历久弥新尧诫丹朱舜教商均弈秋誉满伯符谱存马融作赋韦曜撰论王粲覆局谢安却秦樵夫烂柯梁帝定品积薪待诏仲甫通神百龄早达龙士最尊范捷似仙施缓实稳乾嘉以降国衰祚陨北浸朝韩东渐日本御城擂争棋所名人江户四家首推坊门算砂秀策千年才俊";
            string[] rBase = new string[data.Length];
            for (int i = 0; i < data.Length; i++)
            {
                rBase[i] = data.Substring(i, 1);
            }
            Random rnd = new Random();
            List<int> list = new List<int>();
            foreach (var item in rBase)
            {
                if (list.Count == strlength - 1)
                {
                    break;
                }
                var str = rnd.Next(0, rBase.Length);
                if (!list.Contains(str))
                {
                    list.Add(str);
                }
            }
            var newlist = list.OrderBy(o => o).ToList();
            string result = string.Empty;
            for (int i = 0; i < list.Count; i++)
            {
                result += rBase[list[i]];
            }
            string djyzm = string.Empty;
            List<string> listyz = new List<string>();
            for (int i = 0; i < checkNum; i++)
            {
                listyz.Add(rBase[newlist[i]]);
            }
            LoginYZM yZM = new LoginYZM() { yzm = result, djyzm = String.Join(",", listyz) };
            //LoginCache.GetLoginCache.AddUserLoginYzm(yZM.djyzm, DateTime.Now.ToString("yyyyMMddHHmmssff"));
            return yZM;
        }
    }

    /// <summary>
    /// 用户登录验证码
    /// </summary>
    public class LoginYZM
    {
        /// <summary>
        /// 显示验证码
        /// </summary>
        public string yzm { get; set; }

        /// <summary>
        /// 需要顺序点击验证的验证码
        /// </summary>
        public string djyzm { get; set; }

        /// <summary>
        /// 时间戳
        /// </summary>
        public string timestamp { get; set; }
    }
}
